LaunchTemplate으로 EC2 인스턴스를 생성하는 CloudFormation 코드
안녕하세요 클래스메소드 김재욱(Kim Jaewook) 입니다. 이번에는 LaunchTemplate으로 EC2 인스턴스를 생성하는 CloudFormation 코드를 정리해 봤습니다.
VPC 생성
AWSTemplateFormatVersion: "2010-09-09" Description: VPC Network set Metadata: "AWS::CloudFormation::Interface": ParameterGroups: - Label: default: "Network Configuration" Parameters: - VPCCIDR - PublicSubnetACIDR - PublicSubnetCCIDR - PrivateSubnetACIDR - PrivateSubnetCCIDR ParameterLabels: VPCCIDR: default: "VPC CIDR" PublicSubnetACIDR: default: "PublicSubnetA CIDR" PublicSubnetCCIDR: default: "PublicSubnetC CIDR" PrivateSubnetACIDR: default: "PrivateSubnetA CIDR" PrivateSubnetCCIDR: default: "PrivateSubnetC CIDR" # Input VPC, Subnet Parameters & fix Subnet CIDR Parameters: VPCCIDR: Type: String Default: "10.0.0.0/16" PublicSubnetACIDR: Type: String Default: "10.0.1.0/24" PublicSubnetCCIDR: Type: String Default: "10.0.2.0/24" PrivateSubnetACIDR: Type: String Default: "10.0.11.0/24" PrivateSubnetCCIDR: Type: String Default: "10.0.12.0/24" # Set VPC, InternetGateway, Subnet Resources: VPC: Type: "AWS::EC2::VPC" Properties: CidrBlock: !Ref VPCCIDR EnableDnsSupport: "true" EnableDnsHostnames: "true" InstanceTenancy: default Tags: - Key: Name Value: !Sub "test-vpc" InternetGateway: Type: "AWS::EC2::InternetGateway" Properties: Tags: - Key: Name Value: !Sub "test-igw" InternetGatewayAttachment: Type: "AWS::EC2::VPCGatewayAttachment" Properties: InternetGatewayId: !Ref InternetGateway VpcId: !Ref VPC PublicSubnetA: Type: "AWS::EC2::Subnet" Properties: AvailabilityZone: "ap-northeast-2a" CidrBlock: !Ref PublicSubnetACIDR VpcId: !Ref VPC Tags: - Key: Name Value: !Sub "test-front-subnet-1a" PublicSubnetC: Type: "AWS::EC2::Subnet" Properties: AvailabilityZone: "ap-northeast-2c" CidrBlock: !Ref PublicSubnetCCIDR VpcId: !Ref VPC Tags: - Key: Name Value: !Sub "test-front-subnet-1c" PrivateSubnetA: Type: "AWS::EC2::Subnet" Properties: AvailabilityZone: "ap-northeast-2a" CidrBlock: !Ref PrivateSubnetACIDR VpcId: !Ref VPC Tags: - Key: Name Value: !Sub "test-application-subnet-1a" PrivateSubnetC: Type: "AWS::EC2::Subnet" Properties: AvailabilityZone: "ap-northeast-2c" CidrBlock: !Ref PrivateSubnetCCIDR VpcId: !Ref VPC Tags: - Key: Name Value: !Sub "test-application-subnet-1c" # Route Tables FRONTRTB : Type: "AWS::EC2::RouteTable" Properties: VpcId: !Ref VPC Tags: - Key: Name Value: !Sub "test-front-rtb" APPRTB1A: Type: "AWS::EC2::RouteTable" Properties: VpcId: !Ref VPC Tags: - Key: Name Value: !Sub "test-application-rtb-1a" APPRTB1C: Type: "AWS::EC2::RouteTable" Properties: VpcId: !Ref VPC Tags: - Key: Name Value: !Sub "test-application-rtb-1c" FRONTRTBroute: Type: "AWS::EC2::Route" Properties: RouteTableId: !Ref FRONTRTB DestinationCidrBlock: "0.0.0.0/0" GatewayId: !Ref InternetGateway # Route Tables Subnet Association FRONTRTBAssociation: Type: "AWS::EC2::SubnetRouteTableAssociation" Properties: SubnetId: !Ref PublicSubnetA RouteTableId: !Ref FRONTRTB FRONTRTB2Association: Type: "AWS::EC2::SubnetRouteTableAssociation" Properties: SubnetId: !Ref PublicSubnetC RouteTableId: !Ref FRONTRTB APPRTB1AAssociation: Type: "AWS::EC2::SubnetRouteTableAssociation" Properties: SubnetId: !Ref PrivateSubnetA RouteTableId: !Ref APPRTB1A APPRTB1CAssociation: Type: "AWS::EC2::SubnetRouteTableAssociation" Properties: SubnetId: !Ref PrivateSubnetC RouteTableId: !Ref APPRTB1C # Output Parameters Outputs: # VPC VPC: Value: !Ref VPC Export: Name: VPC VPCCIDR: Value: !Ref VPCCIDR Export: Name: VPCCIDR # Subnet PublicSubnetA: Value: !Ref PublicSubnetA Export: Name: PublicSubnetA PublicSubnetACIDR: Value: !Ref PublicSubnetACIDR Export: Name: PublicSubnetACIDR PublicSubnetC: Value: !Ref PublicSubnetC Export: Name: PublicSubnetC PublicSubnetCCIDR: Value: !Ref PublicSubnetCCIDR Export: Name: PublicSubnetCCIDR PrivateSubnetA: Value: !Ref PrivateSubnetA Export: Name: PrivateSubnetA PrivateSubnetACIDR: Value: !Ref PrivateSubnetACIDR Export: Name: PrivateSubnetACIDR PrivateSubnetC: Value: !Ref PrivateSubnetC Export: Name: PrivateSubnetC PrivateSubnetCCIDR: Value: !Ref PrivateSubnetCCIDR Export: Name: PrivateSubnetCCIDR
해당 코드로 생성되는 리소스는 다음과 같습니다.
- VPC - 10.0.0.0/16
- Public Subnet - 10.0.1.0/24, 10.0.2.0/24
- Private Subnet - 10.0.11.0/24, 10.0.12.0/24
- Route Tables
- Internet Gateway
그리고 다른 Stack에서 해당 리소스의 id와 같은 정보를 불러올 수 있도록 Outputs으로 값을 내보내줍니다.
Security Group 생성
AWSTemplateFormatVersion: "2010-09-09" Description: Create Security Group Resources: # Security Group EC2SecurityGroup1: Type: AWS::EC2::SecurityGroup Properties: GroupName: "launch-template-sg" GroupDescription: "launch-template-sg" VpcId: { "Fn::ImportValue": !Sub "VPC" } SecurityGroupIngress: - IpProtocol: tcp FromPort: 22 ToPort: 22 CidrIp: 0.0.0.0/0 - IpProtocol: tcp FromPort : 80 ToPort : 80 CidrIp: 0.0.0.0/0 Tags: - Key: "Name" Value: launch-template-sg Outputs: EC2SecurityGroup1: Value: !Ref EC2SecurityGroup1 Export: Name: EC2SecurityGroup1
보안 그룹도 별도로 만들어줍니다. 테스트용이기 때문에 22번 포트를 0.0.0.0으로 뚫어놨습니다.
IAM Role 생성
AWSTemplateFormatVersion: "2010-09-09" Description: Create IAM Role Resources: # IAM Role (SSM) SSMIAMRole: Type: AWS::IAM::Role DeletionPolicy: Retain Properties: RoleName: Fn::Sub: "launch-template-ssm-role" AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: "Allow" Principal: Service: - "ec2.amazonaws.com" Action: - "sts:AssumeRole" Path: "/" ManagedPolicyArns: - "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore" SSMInstanceProfile: Type: "AWS::IAM::InstanceProfile" DeletionPolicy: Retain Properties: InstanceProfileName: Fn::Sub: "launch-template-ssm-role" Path: "/" Roles: - Ref: SSMIAMRole Outputs: SSMInstanceProfile: Value: !GetAtt SSMInstanceProfile.Arn Export: Name: SSMInstanceProfile
이어서 IAM Role도 생성합니다.
IAM Role의 경우 AmazonSSMManagedInstanceCore만 추가한 상태입니다.
LaunchTemplate & EC2 인스턴스 생성
AWSTemplateFormatVersion: '2010-09-09' Description: Create LaunchTemplate & EC2 Instance Parameters: LinuxLatestAmi: Type : AWS::SSM::Parameter::Value<AWS::EC2::Image::Id> Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2 Ec2InstanceType: Type: String Default: t3.micro KeyPairName: Description: "EC2 Keypair name." Type: AWS::EC2::KeyPair::KeyName Resources: MyLaunchTemplate: Type: AWS::EC2::LaunchTemplate Properties: LaunchTemplateName: MyLaunchTemplate LaunchTemplateData: IamInstanceProfile: Arn: Fn::ImportValue: !Sub SSMInstanceProfile BlockDeviceMappings: - DeviceName: /dev/xvda Ebs: VolumeType: gp2 VolumeSize: 20 DeleteOnTermination: true Encrypted: true DisableApiTermination: true NetworkInterfaces: - AssociatePublicIpAddress: true DeviceIndex: 0 Groups: - Fn::ImportValue: !Sub EC2SecurityGroup1 SubnetId: { "Fn::ImportValue": !Sub "PublicSubnetA" } ImageId: !Ref LinuxLatestAmi InstanceType: !Ref Ec2InstanceType KeyName: !Ref KeyPairName UserData: !Base64 | #!/bin/bash yum update -y yum install httpd-2.4.51 -y systemctl start httpd systemctl enable httpd httpd -v cp /usr/share/httpd/noindex/index.html /var/www/html/index.html EC2Instance: Type: AWS::EC2::Instance Properties: LaunchTemplate: LaunchTemplateId: !Ref MyLaunchTemplate Version: !GetAtt MyLaunchTemplate.LatestVersionNumber DisableApiTermination: true
LaunchTemplate에서 IAM Role을 불러와서 설정하고, NetworkInterfaces를 통해서 보안 그룹과 서브넷을 설정합니다.
AssociatePublicIpAddress를 true로 설정하고, Public Subnet으로 설정했기 때문에 EC2 인스턴스가 Public Subnet에 생성되겠지만, AssociatePublicIpAddress를 false로 설정하면 Private Subnet에 EC2 인스턴스를 생성할 수 있습니다.
이어서 UserData에 아파치 웹 서버를 설치하는 명령어를 작성합니다.
마지막으로 EC2 인스턴스를 생성하는 코드에 LaunchTemplate을 불러옵니다.
EC2 인스턴스 확인
CloudFormation Stack을 확인해 보면 문제없이 생성된 것을 확인할 수 있습니다.
LaunchTemplate도 CloudFormation에서 설정한 대로 생성 된 것을 확인할 수 있습니다.
LaunchTemplate을 바탕으로 EC2 인스턴스도 생성된 상태이며
EC2 인스턴스의 퍼블릭 아이피로 접속해 보면 아파치 또 한 정상적으로 설치된 것을 확인할 수 있습니다.
본 블로그 게시글을 읽고 궁금한 사항이 있으신 분들은 [email protected]로 보내주시면 감사하겠습니다.